﻿/// ****************************************************************************
/// Author:	        Robin Zhu
/// Create Date:	2012-4-28
/// Purpose: 		权限审核服务的数据库实现版本
/// ****************************************************************************
/// Modify By		Date			Remark
/// Robin           2014-2-18       让内置的admin可以获得授权
/// Robin           2015-1-13       add fields to user action log
/// Robin           2015-2-26       增加GetRolesByUrl方法
/// ****************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RB.DataAccess;
using RB.Utility;
using System.Text.RegularExpressions;

namespace RB.Web.Security.Rdb
{
    /// <summary>
    /// 权限审核服务的数据库实现版本
    /// </summary>
    public class DbAuthorizeService : IAuthorizeService
    {
        private static object _permLocker = new object();
        //private static ILookup<string, Permission> _permissionLookup = null;

        private static Guid? _adminGuid = null;

        protected static Guid? AdminGuid
        {
            get
            {
                if (_adminGuid == null)
                {
                    var admin = (new UserAccountSet()).FirstOrDefault(t => t.IsBuiltin && t.UserName.ToLower() == "admin");
                    if (admin != null)
                    {
                        _adminGuid = admin.Id;
                    }
                    else
                    {
                        _adminGuid = Guid.Empty;
                    }
                }
                return _adminGuid == Guid.Empty ? null : _adminGuid;
            }
        }

        private static IEnumerable<Permission> GetPermissionsByAppCode(string appCode)
        {
            return (new PermissionSet()).Where(p => p.AppCode == appCode);
            //lock (_permLocker)
            //{
            //    if (_permissionLookup == null)
            //    {
            //        _permissionLookup = (new PermissionSet()).ToList().ToLookup(p => p.AppCode);
            //    }
            //    if (_permissionLookup != null)
            //    {
            //        return _permissionLookup[appCode];
            //    }
            //    else
            //    {
            //        return new Permission[0];
            //    }
            //}
        }

        //internal static void ResetPermissionCache()
        //{
        //    lock (_permLocker)
        //    {
        //        _permissionLookup = null;
        //    }
        //}

        public bool IsAllowed(string appCode, Guid userId, string pathAndQuery, string httpMethod)
        {
            bool allow = false;

            AppEntrySet appSet = new AppEntrySet();
            var app = appSet[appCode];
            if (app != null)
            {
                
                string permCode = null;
                Guid roleId;

                if (userId != Guid.Empty && pathAndQuery != null)
                {
                    string path = UrlUtility.RemoveQuery(pathAndQuery);
                    string query = UrlUtility.GetQuery(pathAndQuery);
                    allow = CheckUrlUser2(app.Code, path, query, userId, out roleId, out permCode);
                }
                if (permCode == null)
                {
                    /// 如果Url不存在，则看应用程序的设置是否允许公共用户访问
                    allow = app.IsPublic;
                    if (userId != Guid.Empty)
                    {
                        allow = allow || appSet.IsUserAllowed(appCode, userId);
                    }
                }
            }
            return allow;
        }

        //private bool CheckUrlUser(string path, string httpMethod, Guid userId, out Guid roleId, out string permCode)
        //{
        //    using (Executor exec = DataContext.CreateExecutor())
        //    {
        //        var q = from p in exec.Query<PermissionPath>()
        //                join p1 in exec.Query<RolePermission>().DefaultIfEmpty() on p.PermCode equals p1.PermCode
        //                join p2 in exec.Query<UserAccountRole>().DefaultIfEmpty() on p1.RoleId equals p2.RoleId
        //                where p.Path == path && p.HttpMethod == httpMethod && p2.UserId == userId
        //                select new { p.PermCode, p1.RoleId, p2.UserId };
        //        var result = q.FirstOrDefault();
        //        if (result != null)
        //        {
        //            permCode = result.PermCode;
        //            roleId = result.RoleId;
        //            return userId != Guid.Empty && result.UserId == userId;
        //        }
        //        else
        //        {
        //            permCode = null;
        //            roleId = Guid.Empty;
        //            return false;
        //        }
        //    }
        //}

        public bool Authorize(string appCode, string ticket, string pathAndQuery, string httpMethod)
        {
            Guid userId = Guid.Empty;

            if (ticket != null)
            {
                UserSessionSet sessionSet = new UserSessionSet();
                var session = sessionSet[ticket];
                if (session != null || session.LogoutTime != null)
                {
                    userId = session.UserId;
                }
            }

            AppEntrySet appSet = new AppEntrySet();
            AppEntry app = appSet[appCode];
            //if (userId != Guid.Empty)
            //{
            //    app = appSet.SingleForUser(appCode, userId);
            //}
            //else
            //{
            //    app = appSet[appCode];
            //}

            bool allow = false;

            if (app != null)
            {
                string permCode = null;
                
                Guid roleId = Guid.Empty;

                if (pathAndQuery != null && userId != Guid.Empty)
                {
                    string path = UrlUtility.RemoveQuery(pathAndQuery);
                    string query = UrlUtility.GetQuery(pathAndQuery);
                    
                    allow = CheckUrlUser2(app.Code, path, query, userId, out roleId, out permCode);
                }
                if (permCode == null)
                {
                    /// 如果Url不存在，则看应用程序的设置是否允许公共用户访问
                    allow = app.IsPublic;
                    if (userId != Guid.Empty)
                    {
                        allow = allow || appSet.IsUserAllowed(appCode, userId);
                    }
                }
                if (allow && ticket != null)
                {
                    /// 记录访问日志
                    UserActionLog log = new UserActionLog()
                    {
                        ActionTime = DateTime.Now,
                        Ticket = ticket,
                        UserId = userId,
                        RoleId = roleId,
                        PermCode = permCode,
                        AppCode = appCode,
                        PathAndQuery = pathAndQuery,
                        HttpMethod = httpMethod
                    };
                    UserActionLogSet logSet = new UserActionLogSet();
                    logSet.Add(log);
                }
            }
            if (!allow)
            {
                allow = (AdminGuid == userId);
            }
            return allow;
            
        }

        private bool CheckUrlUser2(string appCode, string path, string query, Guid userId, out Guid roleId, out string permCode)
        {
            bool result = false;
            Permission findPerm = null;
            var permissionList = GetPermissionsByAppCode(appCode);
            foreach (var permission in permissionList)
            {
                if (permission.IsMatch(path, query))
                {
                    findPerm = permission;
                    break;
                }
            }
            if (findPerm != null)
            {
                if (!findPerm.IsPublic)
                {
                    if (userId != Guid.Empty)
                    {
                        roleId = UserRolePermissionCache.GetRoleId(userId, findPerm.Code);

                        result = (roleId != Guid.Empty);
                    }
                    else
                    {
                        roleId = Guid.Empty;
                    }
                }
                else
                {
                    result = true;
                    roleId = Guid.Empty;
                }
                permCode = findPerm.Code;
            }
            else
            {
                roleId = Guid.Empty;
                permCode = null;
            }
            if (!result)
            {
                // 检查是否为内置的admin账号
                result = (AdminGuid == userId);
            }
            return result;
        }


        public AppMenu[] GetAppMenus(string appCode, Guid userId)
        {
            var set = new AppMenuSet();
            var result = set.WhereByAppCode(appCode, userId).ToArray();
            return result;
        }



        public string[] GetPermissionCodesByTicket(string appCode, string ticket)
        {
            var set = new PermissionSet();
            return set.GetPermissionCodeByTicket(appCode, ticket);
        }

        public Permission[] GetAllPermissions(string appCode)
        {
            var set = new PermissionSet();
            return set.Where(t => !t.IsDisabled && t.AppCode == appCode).ToArray();
        }


        public void LogAction(string appCode, string ticket, string permCode, string pathAndQuery, bool authorized, string ipAddress, string httpForwarded)
        {
            var userSession = (new UserSessionSet())[ticket];
            UserActionLog log = new UserActionLog()
            {
                AppCode = appCode,
                Ticket = ticket,
                PermCode = permCode,
                UserId = (userSession != null ? userSession.UserId : Guid.Empty),
                PathAndQuery = pathAndQuery,
                ActionTime = DateTime.Now,
                Unauthorized = !authorized,
                IPAddress = ipAddress,
                HttpForwarded = httpForwarded
            };
            (new UserActionLogSet()).Add(log);
        }


        public string[] GetRolesByUrl(string appCode, string url)
        {
            var perms = GetAllPermissions(appCode);

            string permCode = null;

            foreach(var perm in perms)
            {
                if (perm.IsMatch(url, null))
                {
                    permCode = perm.Code;
                    break;
                }
            }

            if (permCode != null)
            {
                var roles = (new RolePermissionSet()).GetRoles(permCode);
                return roles;
            }
            return new string[0];
        }


        public AppEntry[] GetAppEntries(string ticket)
        {
            var set = new AppEntrySet();
            return set.GetByTicket(ticket);
        }
    }
}
